package xj.graph2d.graph;

import java.awt.geom.Point2D;
import java.io.PrintWriter;
import java.util.Hashtable;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import static xj.graph2d.graph.GraphConstants.*;

public class AbstractGraphEdge <NodeType extends GraphNode>
  extends AbstractGraphComponent 
  implements GraphEdge<NodeType> { 

  public AbstractGraphEdge() {
    super(EDGE);
  }

  public AbstractGraphEdge(NodeType src, NodeType des) {
    super(EDGE);
    this.src = src;
    this.des = des;
  }

  public AbstractGraphEdge(NodeType src, NodeType des, int edgeType) {
    super(EDGE);
    this.src = src;
    this.des = des;
    this.edgeType = edgeType;
  }

  public AbstractGraphEdge(NodeType src, String srcPort, NodeType des,
			   String desPort) {
    super(EDGE);
    this.src = src;
    this.des = des;
    this.sourcePort = srcPort;
    this.destinationPort = desPort;
  }

  public AbstractGraphEdge(NodeType src, String srcPort, NodeType des,
      String desPort, int edgeType) {
    super(EDGE);
    this.src = src;
    this.des = des;
    this.sourcePort = srcPort;
    this.destinationPort = desPort;
    this.edgeType = edgeType;
  }

  @Override
  public int hashCode() {
    int result = 0;
    if (src != null) {
      result = (src.hashCode() << 16);
    }
    if (des != null) {
      result += des.hashCode();
    }
    return result;
  }

  @Override
  public int getType() {
    return EDGE;
  }

  @Override
  public boolean isEdge() {
    return true;
  }

  public NodeType getSource() {
    return src;
  }

  public NodeType getDestination() {
    return des;
  }

  public void setSource(NodeType node) {
    src = node;
  }

  public void setDestination(NodeType node) {
    des = node;
  }

  public boolean isHyperEdge() {
    return ((src != null && src instanceof HyperGraphNode) || 
	    (des != null && des instanceof HyperGraphNode));
  }

  public int getEdgeType() {
    return edgeType;
  }

  public void setEdgeType(int type) {
    edgeType = type;
  }

  public boolean isDirected() {
    return directed;
  }

  public void setDirected(boolean directed) {
    this.directed = directed;
  }

  public void setEnds(Point2D p1, Point2D p2) {
    this.p1 = p1;
    this.p2 = p2;
  }

  public Point2D getEnd1() {
    return p1;
  }

  public Point2D getEnd2() {
    return p2;
  }

  public void adjustEnds() {
  }

  public String getSourcePort() {
    return sourcePort;
  }

  public String getDestinationPort() {
    return destinationPort;
  }

  public void setSourcePort(String port) {
    sourcePort = port;
  }

  public void setDestinationPort(String port) {
    destinationPort = port;
  }

  public String getEdgeString() {
    return "(" + (src != null ? src.getId() : "-") + ","
	+ (des != null ? des.getId() : "-") + ")";
  }

  /**
         * Get the String rendition of the edge.
         * 
         * @return the string rendition of the edge, quoted as needed.
         */
  @Override
  public String toString() {
    if (canonName == null) {
      String tail = null;
      String head = null;

      if (src != null) {
	if (sourcePort == null) {
	  tail = src.toString();
	} else {
	  tail = src.toString() + ":" + GraphUtilities.canonString(sourcePort);
	}
      } else {
	tail = "null";
      }
      if (des != null) {
	if (destinationPort == null) {
	  head = des.toString();
	} else {
	  head = des.toString() + ":"
	      + GraphUtilities.canonString(destinationPort);
	}
      } else {
	head = "null";
      }

      Graph g = getGraph();
      if (g == null || g.isDirected()) {
	canonName = tail + " -> " + head;
      } else {
	canonName = tail + " -- " + head;
      }
    }
    return (canonName);
  }

  public void setSourceConnector(Connector c) {
    sourceConnector = c;
  }

  public Connector getSourceConnector() {
    return sourceConnector;
  }

  public void setDestinationConnector(Connector c) {
    destinationConnector = c;
  }

  public Connector getDestinationConnector() {
    return destinationConnector;
  }

  /**
         * Print the edge description to the provided stream.
         * 
         * @param out
         *                the output stream for writing the description.
         */
  public void printEdge(PrintWriter out) {
    this.printElement(out);
  }

  /* Added by Naveen Vemuri, May 3, 2003. */
  public Element createXGMMLElement(Element graphElement, Document doc,
				    Hashtable nodeHash, int id) {
    Element edgeElement = doc.createElement(XGMML_EDGE_TAG);
    /* can use attributes id, name, label, and labelanchor. */
    edgeElement.setAttribute("id", new Integer(id).toString());
    edgeElement.setAttribute("label", "1"); // default label is 1.
    /* must use attributes source, target (both id numbers) and weight. */
    edgeElement.setAttribute("weight", "1"); // default weight is 1.
    edgeElement.setAttribute("source", (String) nodeHash.get(this.getSource()));
    edgeElement.setAttribute("target", (String) nodeHash.get(this.getDestination()));

    graphElement.appendChild(edgeElement);

    return graphElement;
  }

  /* End of additions. */

  @Override
  protected String getDefaultNamePrefix() {
    if (prefix != null) {
      return prefix + "e";
    } else {
      return "e";
    }
  }

  protected static int _id = 1;

  @Override
  protected int getNextId() {
    return AbstractGraphEdge._id++;
  }

  protected NodeType src;

  protected NodeType des;

  protected int edgeType;

  protected boolean directed = true;

  protected String sourcePort = null;

  protected String destinationPort = null;

  protected Point2D p1, p2;

  protected Connector sourceConnector = null;

  protected Connector destinationConnector = null;

}
